#include <device/map.h>
#include <utils.h>
#include <isa.h>
// static uint32_t *clint_port_base = NULL;
static uint32_t *clint_port_msip = NULL;
static uint64_t *clint_port_mtimecmp = NULL;
static uint64_t *clint_port_mtime = NULL;
#define MSIP_OFFSET       0
#define MTIMECMP_OFFSET   0x4000
#define MTIME_OFFSET      0xBFF8
static void clint_io_msip_handler(uint32_t offset, int len, bool is_write) {
    assert(offset < 4);
    if(is_write) {
      uint32_t temp = *clint_port_msip;
      if(temp&0x01) csr_set_bits(MIP_ADDR,MIP_MSIP_MASK);
      else csr_clear_bits(MIP_ADDR,MIP_MSIP_MASK);
    }
}
static void clint_io_mtimecmp_handler(uint32_t offset, int len, bool is_write) {
    assert(offset < 8);
    if(is_write) csr_clear_bits(MIE_ADDR,MIE_MTIE_MASK);
}

// static void clint_io_handler(uint32_t offset, int len, bool is_write) {
// #define BETWEEN(_v,_start,_len) (_v >= _start && _v < _start+_len)
//   assert(BETWEEN(offset,MSIP_OFFSET,8) || BETWEEN(offset,MTIMECMP_OFFSET,8) || BETWEEN(offset,MTIME_OFFSET,8) );
//   if(is_write) {
//     if(BETWEEN(offset,MTIMECMP_OFFSET,8))
//     {
//       csr_clear_bits(MIE_ADDR,MIE_MTIE_MASK);
//     }
//     if(BETWEEN(offset,MSIP_OFFSET,8))
//     {
      
//       uint32_t *msip = clint_port_base;
//       uint32_t temp = *msip;
//       if(temp&0x01) csr_set_bits(MIP_ADDR,MIP_MSIP_MASK);
//       else csr_clear_bits(MIP_ADDR,MIP_MSIP_MASK);
//     }
//   }
  

// #undef BETWEEN
// }



void init_clint() {
//   clint_port_base = (uint32_t *)new_space(0xC000);
  clint_port_msip = (uint32_t *)new_space(4);
  clint_port_mtimecmp = (uint64_t *)new_space(8);
  clint_port_mtime = (uint64_t *)new_space(8);
  memset(clint_port_msip,0,4);
  memset(clint_port_mtimecmp,0,8);
  memset(clint_port_mtime,0,8);
//   assert(clint_port_base);
//   memset(clint_port_base,0,0xC000);
#ifdef CONFIG_HAS_PORT_IO
  add_pio_map ("clint", CONFIG_CLINT_PORT, clint_port_base, 0xC000, clint_io_handler);
  add_mmio_map("clint", CONFIG_CLINT_PORT, clint_port_msip, 4, clint_io_handler);
  add_mmio_map("clint", CONFIG_CLINT_PORT, clint_port_mtimecmp, 8, clint_io_handler);
  add_mmio_map("clint", CONFIG_CLINT_PORT, clint_port_mtime, 8, clint_io_handler);
#else
//   add_mmio_map("clint", CONFIG_CLINT_MMIO, clint_port_base, 0xC000, clint_io_handler);
  add_mmio_map("clint_MSIP", CONFIG_CLINT_MMIO+MSIP_OFFSET, clint_port_msip, 4, clint_io_msip_handler);
  add_mmio_map("clint_MTIMECMP", CONFIG_CLINT_MMIO+MTIMECMP_OFFSET, clint_port_mtimecmp, 8, clint_io_mtimecmp_handler);
  add_mmio_map("clint_MTIME", CONFIG_CLINT_MMIO+MTIME_OFFSET, clint_port_mtime, 8, NULL);
#endif
}
void clint_update() {
  // uint32_t *msip = clint_port_base;

  bool enable = csr_read(MSTATUS_ADDR)&MSATUS_MIE_MASK && csr_read(MIE_ADDR)&MIE_MTIE_MASK;
  // enable = true;
  if(enable && *clint_port_mtime >= *clint_port_mtimecmp) 
  {
    // Log("CLINT:interrupt");
    /*Drive the MTIP bit in mip CSR of RISC-V cores. Indicates a timer interrupt is pending.*/
    csr_set_bits(MIP_ADDR,MIP_MTIP_MASK);
    cpu.pc = isa_raise_intr(MCAUSE_TIM_INT_M,cpu.pc);
    difftest_skip_ref();
    *clint_port_mtimecmp = *clint_port_mtime + 1000000000;
  }
  *clint_port_mtime = *clint_port_mtime + 1;
}
